package dawn.jxufe.community.service;

import dawn.jxufe.community.dao.AlphaDao;
import dawn.jxufe.community.dao.DiscussPostMapper;
import dawn.jxufe.community.dao.UserMapper;
import dawn.jxufe.community.entity.DiscussPost;
import dawn.jxufe.community.entity.User;
import dawn.jxufe.community.util.CommunityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.Date;

/**
 * @author dawn21
 * @version 1.0
 * @date 2022/6/23 16:40
 * @component
 */
@Service
//@Scope("prototype") //bean多个实例，每次访问bean就会创建一个新的实例，但是一般就是一个bean。
public class AlphaService {

    @Autowired
    private AlphaDao alphaDao;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private DiscussPostMapper discussPostMapper;

    // 事务管理的bean，感觉除了自己写的代码和springboot的代码，其他的得用@recourse才有用。
    @Resource
    private TransactionTemplate transactionTemplate;

    private static final Logger logger = LoggerFactory.getLogger(AlphaService.class);

    //容器管理bean的初始化和销毁

    //构造器方法
    public AlphaService(){
//        System.out.println("实例化AlphaService");
    }

    //初始化方法,有这个注解就会自动调用这个初始化方法
    //初始化方法一般在构造器方法之后调用
    @PostConstruct
    public void init(){
//        System.out.println("初始化AlphaService");
    }

    //销毁方法
    @PreDestroy //销毁对象之前调用这个方法。
    public void destroy(){
//        System.out.println("销毁AlphaService");
    }

    public String find(){
        return alphaDao.select();
    }

    // 注册用户，业务：先增加user，然后系统自动让用户发送一条帖子(新人打招呼帖子)。这就是一个业务包含两个增加的操作。
    // 这一个完整的业务要保证事务性，在这个杜撰的业务来演示。
    // 编写两种方法来演示这两种事务的管理方式。两种方法。
    // 通过注解方式，让它报错回滚。不同级别的异常要使用不同级别的事务管理，当然，也要注意效率。
    // 事务的传播特性：a业务方法调用了b业务方法，a和b都可能使用事务管理，b是使用a还是b的事务管理，这就是事务的传播特性来管理。
    // REQUIRED: 支持当前(外部)事务，也就是a事务。如果a没有事务，就创建一个新的事务。
    // REQUIRES_NEW: 创建一个新的事务，并且暂停当前(外部)事务，也就是a事务。
    // NESTED: 如果存在当前事务，则嵌套在当前事务中执行。
    // 嵌套: 也就是b有独立的提交和回滚，如果a事务不存在就和REQUIRED一样了。
    // 结论: 事务管理就是为了在满足效率的情况下，使用正确的隔离级别，保证在访问数据库发生异常时可以正确回滚。
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public Object save1() {
        // 增加用户
        User user = new User();
        user.setName("username1002");
        user.setEmail("user1002@qq.com");
        user.setHeaderUrl("https://images.nowcoder.com/head/99t.png");
        user.setSalt(CommunityUtil.generateUUID().substring(0,5));
        user.setPassword(CommunityUtil.md5("123456" + user.getSalt()));
        user.setCreateTime(new Date());
        userMapper.insertUser(user); // 插入以后mybatis就会向数据库要user的id，所以insert之后就有id了。

        // 增加帖子
        DiscussPost discussPost = new DiscussPost();
        discussPost.setCreateTime(new Date());
        discussPost.setTitle("新人入住！");
        discussPost.setContent("新人入住，学习、求职！");
        discussPost.setUserId(user.getId());
        discussPostMapper.insertDiscussPost(discussPost);

        // 人为制造异常，看看会不会回滚，查看事务管理会不会起效果。
        Integer.valueOf("abc");
        return "ok";
    }

    // 用transactionTemplate这个bean管理事务。
    public Object save2() {
        // 设置隔离级别和传播特性。
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 返回回调方法。
        return transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                // 在这里实现本方法的逻辑。
                // transactionTemplate调用逻辑，实现事务管理。
                // 增加用户
                User user = new User();
                user.setName("username1003");
                user.setEmail("user1003@qq.com");
                user.setHeaderUrl("https://images.nowcoder.com/head/98t.png");
                user.setSalt(CommunityUtil.generateUUID().substring(0,5));
                user.setPassword(CommunityUtil.md5("123456" + user.getSalt()));
                user.setCreateTime(new Date());
                userMapper.insertUser(user); // 插入以后mybatis就会向数据库要user的id，所以insert之后就有id了。

                // 增加帖子
                DiscussPost discussPost = new DiscussPost();
                discussPost.setCreateTime(new Date());
                discussPost.setTitle("新人入住呀！");
                discussPost.setContent("新人入住，学习、求职！");
                discussPost.setUserId(user.getId());
                discussPostMapper.insertDiscussPost(discussPost);

                // 人为制造异常，看看会不会回滚，查看事务管理会不会起效果。
                Integer.valueOf("abc");
                return "ok";
            }
        });
    }

    // 让该方法在多线程环境下被异步地调用。这个就相当于一个线程体。
    @Async
    public void execute1() {
        logger.debug("====execute1====");
    }

    //@Scheduled(initialDelay = 10000, fixedDelay = 1000)
    public void execute2() {
        logger.debug("====execute2====");
    }

}
